package org.Yiran.timetale_re.network;

import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.network.NetworkEvent;
import net.minecraftforge.network.NetworkRegistry;
import net.minecraftforge.network.simple.SimpleChannel;
import org.Yiran.timetale_re.Timetale_re;

import java.util.function.BiConsumer;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * 网络通道管理类
 * <p>
 * 该类负责初始化和管理模组的网络通信通道，以及注册各种自定义数据包处理器。
 * 使用Forge的SimpleChannel系统来处理客户端和服务端之间的网络通信。
 * </p>
 */
public class NetworkChannel {
    
    // ==============================
    // 实例变量
    // ==============================

    /**
     * 网络通道实例，用于发送和接收数据包
     * <p>
     * 通过NetworkRegistry.newSimpleChannel创建的简单网络通道，
     * 用于在客户端和服务端之间传输自定义数据包。
     * </p>
     */
    public static SimpleChannel CHANNEL;

    /**
     * 数据包ID计数器，保证每个数据包都有唯一的ID
     * <p>
     * 用于为注册的每个数据包类型分配唯一的标识符。
     * </p>
     */
    private static int ID = 0;

    /**
     * 网络协议版本号，用于验证客户端和服务端是否兼容
     * <p>
     * 确保客户端和服务端使用相同版本的网络协议，
     * 防止因版本不匹配导致的通信问题。
     * </p>
     */
    private static final String VERSION = "1.0";

    // ==============================
    // ID管理方法
    // ==============================

    /**
     * 获取下一个可用的数据包ID并递增计数器
     * <p>
     * 返回当前ID值并将其递增，确保每个注册的数据包都有唯一的ID。
     * </p>
     *
     * @return 当前ID值（使用后自动+1）
     */
    public static int nextID(){
        return ID++;
    }

    // ==============================
    // 消息注册方法
    // ==============================

    /**
     * 注册所有网络消息类型
     * <p>
     * 在模组初始化阶段调用此方法，注册所有需要的自定义数据包类型。
     * 包括飞行符文同步、冷却状态同步、长桌物品同步等。
     * </p>
     */
    public static void registerMessage() {
        // 定义一个新的简单网络通道
        CHANNEL = NetworkRegistry.newSimpleChannel(
                ResourceLocation.fromNamespaceAndPath(Timetale_re.MODID, "network"),
                () -> VERSION,
                s -> s.equals(VERSION),
                s -> s.equals(VERSION)
        );

        // 注册功能符文数据包 - 用于同步飞行符文实体的状态
        registerPacket(
                SCResponsePacket.RunePacket.class,
                SCResponsePacket.RunePacket::new,
                SCResponsePacket.RunePacket::handle
        );

        // 注册冷却同步数据包 - 用于在客户端和服务端间同步物品冷却状态
        registerPacket(
                SCResponsePacket.CooldownPacket.class,
                SCResponsePacket.CooldownPacket::new,
                SCResponsePacket.CooldownPacket::handle
        );

        // 注册长桌物品同步数据包 - 同步长桌方块实体上的物品显示状态
        registerPacket(
                SCResponsePacket.LongTableItemSyncPacket.class,
                SCResponsePacket.LongTableItemSyncPacket::new,
                SCResponsePacket.LongTableItemSyncPacket::handle
        );

        // 注册符文图纸手册同步数据包 - 同步玩家手中符文蓝图书的NBT数据
        registerPacket(
                SCResponsePacket.RuneBlueprintBookSyncPacket.class,
                SCResponsePacket.RuneBlueprintBookSyncPacket::new,
                SCResponsePacket.RuneBlueprintBookSyncPacket::handle
        );

        // 注册符文图纸手册视图数据包 - 在客户端打开符文蓝图书预览界面
        registerPacket(
                SCResponsePacket.RuneBlueprintBookViewPacket.class,
                SCResponsePacket.RuneBlueprintBookViewPacket::new,
                SCResponsePacket.RuneBlueprintBookViewPacket::handle
        );

        // 注册符文制作熟练度同步数据包 - 同步玩家的符文制作熟练度信息
        registerPacket(
                SCResponsePacket.RuneProficiencySyncPacket.class,
                SCResponsePacket.RuneProficiencySyncPacket::new,
                SCResponsePacket.RuneProficiencySyncPacket::handle
        );

        registerPacket(
                SCResponsePacket.RemoveSpecificModulePacket.class,
                SCResponsePacket.RemoveSpecificModulePacket::new,
                SCResponsePacket.RemoveSpecificModulePacket::handle
        );
    }

    // ==============================
    // 通用注册方法
    // ==============================

    /**
     * 通用数据包注册方法
     * <p>
     * 通过指定数据包类、解码器和处理器来注册一个新的数据包类型。
     * 简化了数据包注册过程，避免重复代码。
     * </p>
     *
     * @param clazz   数据包类
     * @param decoder 解码器函数，用于从网络数据缓冲区创建数据包实例
     * @param handler 处理器函数，用于处理接收到的数据包
     * @param <T>     数据包类型
     */
    private static <T extends SimplePacket> void registerPacket(
            Class<T> clazz,
            Function<FriendlyByteBuf, T> decoder,
            BiConsumer<T, Supplier<NetworkEvent.Context>> handler) {
        CHANNEL.registerMessage(
                nextID(),
                clazz,
                SimplePacket::encode,
                decoder,
                handler
        );
    }
}